package com.demo.gateway.config;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.common.utils.StringUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 监听Nacos配置变化，动态刷新路由
 */
@Component
public class NacosRouteRefresher implements ApplicationEventPublisherAware {

    private final NacosConfigManager nacosConfigManager;
    private final NacosConfigProperties nacosConfigProperties;
    private final RouteDefinitionWriter routeDefinitionWriter;
    private ApplicationEventPublisher publisher;
    private final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
    // Nacos中路由配置的DataId
    private static final String ROUTE_DATA_ID = "gateway-demo-routes.yaml";

    public NacosRouteRefresher(NacosConfigManager nacosConfigManager,
                              NacosConfigProperties nacosConfigProperties,
                              RouteDefinitionWriter routeDefinitionWriter) {
        this.nacosConfigManager = nacosConfigManager;
        this.nacosConfigProperties = nacosConfigProperties;
        this.routeDefinitionWriter = routeDefinitionWriter;
    }

    @PostConstruct
    public void init() throws NacosException {
        // 初始加载路由
        loadRouteConfig();
        // 监听配置变化
        nacosConfigManager.getConfigService().addListener(
                ROUTE_DATA_ID,
                nacosConfigProperties.getGroup(),
                new Listener() {
                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        loadRouteConfig(); // 配置变化时重新加载
                    }
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }
                }
        );
    }

    // 从Nacos加载路由配置并刷新
    private void loadRouteConfig() {
        try {
            // 1. 清除旧路由
//            routeDefinitionWriter.delete(Mono.just("*")).subscribe();
            
            // 2. 从Nacos获取新路由配置（这里简化处理，实际需解析yaml为List<RouteDefinition>）
            String config = nacosConfigManager.getConfigService()
                    .getConfig(ROUTE_DATA_ID, nacosConfigProperties.getGroup(), 5000);
            
            // 3. 解析配置并添加新路由（实际需用Jackson解析config为RouteDefinition列表）
            List<RouteDefinition> routes = parseRouteConfig(config); 
            routes.forEach(route -> routeDefinitionWriter.save(Mono.just(route)).subscribe());
            
            // 4. 发布路由刷新事件
            publisher.publishEvent(new org.springframework.cloud.gateway.event.RefreshRoutesEvent(this));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 解析Nacos配置为路由定义（实际实现需用Jackson处理）
    private List<RouteDefinition> parseRouteConfig(String yamlConfig) {
        try {
            // 1. 将 YAML 字符串解析为嵌套 Map（对应配置结构）
            Map<String, Object> configMap = yamlMapper.readValue(yamlConfig, new TypeReference<Map<String, Object>>() {});

            // 2. 提取 spring.cloud.gateway.routes 节点下的配置
            Map<String, Object> spring = (Map<String, Object>) configMap.get("spring");
            Map<String, Object> cloud = (Map<String, Object>) spring.get("cloud");
            Map<String, Object> gateway = (Map<String, Object>) cloud.get("gateway");
            List<Map<String, Object>> routeMaps = (List<Map<String, Object>>) gateway.get("routes");

            // 3. 转换为 RouteDefinition 列表
            return routeMaps.stream()
                    .map(this::convertToRouteDefinition)
                    .collect(Collectors.toList());
        } catch (Exception e) {
            throw new RuntimeException("解析路由配置失败", e);
        }
    }

    /**
     * 将单个路由的 Map 配置转换为 RouteDefinition 对象
     */
    private RouteDefinition convertToRouteDefinition(Map<String, Object> routeMap) {
        RouteDefinition route = new RouteDefinition();

        // 设置路由基本信息
        route.setId((String) routeMap.get("id"));
        route.setUri(URI.create((String) routeMap.get("uri")));

        // 处理路由优先级（可选，默认0）
        if (routeMap.containsKey("order")) {
            route.setOrder(Integer.parseInt(routeMap.get("order").toString()));
        }

        // 解析断言（predicates）
        List<Object> predicates = (List<Object>) routeMap.get("predicates");
        if (predicates != null) {
            for (Object object : predicates) {
                PredicateDefinition predicate = new PredicateDefinition();
                if(object instanceof String){
                    String predicateStr = (String) object;
                    String[] parts = StringUtils.split(predicateStr, "="); // 按第一个 "=" 拆分
                    if (parts.length == 2) {
                        predicate.setName(parts[0].trim()); // 断言名称（如 Path）
                        // 设置参数（Spring Gateway 断言通常用 "pattern" 作为参数名）
                        predicate.getArgs().put("pattern", parts[1].trim());
                    } else {
                        throw new IllegalArgumentException("无效的断言格式: " + predicateStr);
                    }
                }
                route.getPredicates().add(predicate);
            }
        }

        // 解析过滤器（filters）
        List<Object> filters = (List<Object>) routeMap.get("filters");
        if (filters != null) {
//            filters.forEach(filterMap -> {
            for (Object filterMap : filters) {
                FilterDefinition filter = new FilterDefinition();
                if(filterMap instanceof String){

                    String predicateStr = (String) filterMap;
                    String[] parts = StringUtils.split(predicateStr, "="); // 按第一个 "=" 拆分
                    if (parts.length == 2) {
                        filter.setName(parts[0].trim()); // 断言名称（如 Path）
                        // 设置参数（Spring Gateway 断言通常用 "pattern" 作为参数名）
                        filter.getArgs().put("pattern", parts[1].trim());
                    } else {
                        throw new IllegalArgumentException("无效的断言格式: " + predicateStr);
                    }
                } else if (filterMap instanceof Map) {
                    // 过滤器格式：[{ "name": "StripPrefix", "args": { "_genkey_0": "1" } }]
                    String name = (String) ((Map<String, String>) filterMap).get("name");
                    Map<String, Object> args =(  Map<String, Object>)((Map<String, Object>) filterMap).get("args");
                    filter.setName(name);
                    Map<String, String> stringStringHashMap = new HashMap<>();

                    for (Map.Entry<String, Object> stringObjectEntry : args.entrySet()) {
                        String key = stringObjectEntry.getKey();
                        Object value = stringObjectEntry.getValue();
                        if(value instanceof Integer){
                            stringStringHashMap.put(key, value.toString());
                        }
                    }
                    filter.setArgs(stringStringHashMap);
                }
                route.getFilters().add(filter);
            }

//            });
        }

        return route;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }
}